// tcp_thread_server.hpp
#pragma once
#include <functional>
#include <pthread.h>
#include "tcp_socket.hpp"

typedef std::function<void(const std::string &, std::string *)> Handler;

struct ThreadArg
{
    TcpSocket new_sock;
    std::string ip;
    uint16_t port;
    Handler handler;
};

class TcpThreadServer
{
public:
    TcpThreadServer(const std::string &ip, uint16_t port) : _ip(ip), _port(port)
    {
    }
    bool Start(Handler handler)
    {
        // 1. 创建 socket;
        CHECK_RET(_listen_sock.Socket());
        // 2. 绑定端口号
        CHECK_RET(_listen_sock.Bind(_ip, _port));
        // 3. 进行监听
        CHECK_RET(_listen_sock.Listen(5));
        // 4. 进入循环
        for (;;)
        {
            // 5. 进行 accept
            ThreadArg *arg = new ThreadArg();
            arg->handler = handler;
            bool ret = _listen_sock.Accept(&arg->new_sock, &arg->ip, &arg->port);
            if (!ret)
            {
                continue;
            }
            printf("[client %s:%d] connect\n", arg->ip.c_str(), arg->port);
            // 6. 创建新的线程完成具体操作
            pthread_t tid;
            pthread_create(&tid, NULL, ThreadEntry, arg);
            pthread_detach(tid);
        }
        return true;
    }
    // 这里的成员函数为啥非得是 static?
    static void *ThreadEntry(void *arg)
    {
        // C++ 的四种类型转换都是什么?
        ThreadArg *p = reinterpret_cast<ThreadArg *>(arg);
        ProcessConnect(p);
        // 一定要记得释放内存!!! 也要记得关闭文件描述符
        p->new_sock.Close();
        delete p;
        return NULL;
    }
    // 处理单次连接. 这个函数也得是 static
    static void ProcessConnect(ThreadArg *arg)
    {
        // 1. 循环进行读写
        for (;;)
        {
            std::string req;
            // 2. 读取请求
            bool ret = arg->new_sock.Recv(&req);
            if (!ret)
            {
                printf("[client %s:%d] disconnected!\n", arg->ip.c_str(), arg->port);
                break;
            }
            std::string resp;
            // 3. 根据请求计算响应
            arg->handler(req, &resp);
            // 4. 发送响应
            arg->new_sock.Send(resp);
            printf("[client %s:%d] req: %s, resp: %s\n", arg->ip.c_str(), arg->port, req.c_str(), resp.c_str());
        }
    }

private:
    TcpSocket _listen_sock;
    std::string _ip;
    uint16_t _port;
};